/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.merak.core.web;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.fileupload.FileItem;
import org.merak.core.Application;
import org.merak.core.model.parameter.Parameter;

/**
 * The Upload Value is the filename, since it instances the form field in
 * the browser. It makes filename-oriented the methods getValue(),isFilled(),
 * isNull(), hasValue() and toString() so they are consistent with each other.
 * Otherwise, to get Upload Content, please call getFile(),
 * getInputStream(), getBytes() or getString().
 */

public class WebParameterFile extends Parameter implements WebParameter {

	//~ Attributes ///////////////////////////////////////////////////////////////
	//****************************************************************************
	private FileItem fileItem = null;
	private String   fileName = null;

	//~ Constructors /////////////////////////////////////////////////////////////
	//****************************************************************************
	protected WebParameterFile(String name) {

		// Initialize super class
		super(name,"");

	}
	//****************************************************************************
	protected WebParameterFile(FileItem fileItem) {

		// Super Initialize: Parameter class
		super(
			fileItem.getFieldName(),                // Parameter Name : the form parameter name
			new File(fileItem.getName()).getName()  // Parameter Value: the file name
		);

		// Check FileItem properties
		if ( fileItem.isFormField() ) {
			throw new IllegalArgumentException("FileItem cannot be a form field; should be a file field.");
		}

		// Initialize member attributes
		this.fileItem = fileItem;
		this.fileName = super.getValue();

	}

	//~ Methods //////////////////////////////////////////////////////////////////
	//****************************************************************************
	@Override
	public boolean isUpload() {
		return true;
	}
	//****************************************************************************
	@Override
	public String getFileName() {
		return this.fileName;
	}
	//****************************************************************************
	@Override
	public String getFileType() {

		int typeOffset = this.fileName.lastIndexOf('.');
		return  typeOffset == -1
				? ""
				: this.fileName.substring(typeOffset+1);

	}
	//****************************************************************************
	@Override
	public boolean hasFileType(String type) {

		return this.fileName.endsWith(type);

	}
	//****************************************************************************
	@Override
	public long getLength() {
		return this.fileItem.getSize();
	}
	//****************************************************************************
	/** Uploads and write the data to a temporary file.
	 * @throws Exception if the temporary file cannot be created or written to the disk */
	@Override
	public File getFile() throws Exception {

		File uploadedFile = Application.io.createTempFile( this.getFileName() , this.getFileType() );
		this.fileItem.write(uploadedFile);
		return uploadedFile;

	}
	//****************************************************************************
	/** Uploads and write the data to the specified file.
	 * @throws Exception if the uploaded data cannot be written to the disk */
	@Override
	public File getFile(File file) throws Exception {
		this.fileItem.write(file);
		return file;
	}
	//****************************************************************************
	/** Uploads and returns a byte array.
	 * @throws IOException if the uploaded data cannot be written to the byte array */
	@Override
	public byte[] getBytes() {
		return this.fileItem.get();
	}
	//****************************************************************************
	/** Uploads and returns an input stream.
	 * @throws IOException if the uploaded data cannot be written to the OutputStream */
	@Override
	public InputStream getInputStream() throws IOException {
		return this.fileItem.getInputStream();
	}
	//****************************************************************************
	/** Uploads and write the data to the specified output stream.
	 * @throws IOException if the uploaded data cannot be written to the OutputStream */
	@Override
	public void writeToOutputStrem(OutputStream out) throws IOException {

		/* Calculate buffer size as 1% of the file size,
		 * with maximum size of 100KB */
		int bufferSize;
		if (this.getLength() < 1024) {                 // FileSize < 1KB
			bufferSize = (int)this.getLength();        // buffer < 1KB
		}
		else {
			bufferSize = (this.getLength()>10240000)   // FileSize > 10MB
			? 1024 + 102400                            // buffer is 1KB + 100KB
			: 1024 + (int) (this.getLength() * 0.01d); // buffer is 1KB + 1% of FileSize
		}

        // Create the buffer and read the file content
		InputStream in     = this.fileItem.getInputStream();
		byte[]      buffer = new byte[bufferSize];
		int         length = in.read(buffer);

		while ( length > 0 ) {
			out.write(buffer,0,length);
			length = in.read(buffer);
		}
		out.flush();

	}
	//****************************************************************************
	@Override
	public boolean equals(Object other) {

		if (other==null) return false;

		return (other instanceof WebParameterFile)
		       && ((WebParameterFile)other).hasValue(this.getValue());

	}
	//****************************************************************************

}
